package org.yiqixue.accounts.service.impl;

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.yiqixue.accounts.constants.AccountsConstants;
import org.yiqixue.accounts.dto.AccountsDto;
import org.yiqixue.accounts.dto.CustomerDto;
import org.yiqixue.accounts.entity.Accounts;
import org.yiqixue.accounts.entity.Customer;
import org.yiqixue.accounts.entity.Transaction;
import org.yiqixue.accounts.exception.CustomerAlreadyExistsException;
import org.yiqixue.accounts.exception.ResourceNotFoundException;
import org.yiqixue.accounts.mapper.AccountsMapper;
import org.yiqixue.accounts.mapper.CustomerMapper;
import org.yiqixue.accounts.repository.AccountsRepository;
import org.yiqixue.accounts.repository.CustomerRepository;
import org.yiqixue.accounts.repository.TransactionRepository;
import org.yiqixue.accounts.service.IAccountsService;
import org.yiqixue.accounts.service.TransactionService;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Random;

@Service
@AllArgsConstructor
public class AccountsService implements IAccountsService {

    private AccountsRepository accountsRepo;
    private CustomerRepository customerRepo;
    private TransactionRepository transactionRepo;
    private TransactionService transactionService;

    public void createAccount(CustomerDto customerDto) {
        Customer customer = CustomerMapper.mapToCustomer(customerDto, new Customer());
        Optional<Customer> optionalCustomer = customerRepo.findByMobileNumber(customerDto.getMobileNumber());

        if (optionalCustomer.isPresent()) {
            throw new CustomerAlreadyExistsException("同一手机号的客户已经存在" + customerDto.getMobileNumber());
        }

        Customer savedCustomer = customerRepo.save(customer);
        accountsRepo.save(createNewAccount(savedCustomer));
    }

    public CustomerDto fetchAccount(String mobileNumber) {
        Customer customer = customerRepo.findByMobileNumber(mobileNumber).orElseThrow(() -> new ResourceNotFoundException("Customer", "mobileNumber", mobileNumber));
        Accounts accounts = accountsRepo.findByCustomerId(customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Account", "customerId", customer.getCustomerId().toString()));
        CustomerDto customerDto = CustomerMapper.mapToCustomerDto(customer, new CustomerDto());
        customerDto.setAccountsDto(AccountsMapper.mapToAccountsDto(accounts, new AccountsDto()));
        return customerDto;
    }

    public boolean updateAccount(CustomerDto customerDto) {
        boolean isUpdated = false;
        AccountsDto accountsDto = customerDto.getAccountsDto();
        if (accountsDto != null) {
            Accounts accounts = accountsRepo.findById(accountsDto.getAccountNumber()).orElseThrow(() -> new ResourceNotFoundException("Account", "AccountNumber", accountsDto.getAccountNumber().toString()));
            AccountsMapper.mapToAccounts(accountsDto, accounts);
            accounts = accountsRepo.save(accounts);

            Long customerId = accounts.getCustomerId();
            Customer customer = customerRepo.findById(customerId).orElseThrow(() -> new ResourceNotFoundException("Customer", "CustomerID", customerId.toString()));
            CustomerMapper.mapToCustomer(customerDto, customer);
            customerRepo.save(customer);
            isUpdated = true;
        }
        return isUpdated;
    }

    public boolean deleteAccount(String mobileNumber) {
        Customer customer = customerRepo.findByMobileNumber(mobileNumber).orElseThrow(() -> new ResourceNotFoundException("Customer", "mobileNumber", mobileNumber));
        accountsRepo.deleteByCustomerId(customer.getCustomerId());
        customerRepo.deleteById(customer.getCustomerId());
        return true;
    }

    private Accounts createNewAccount(Customer customer) {
        Accounts newAccount = new Accounts();
        newAccount.setCustomerId(customer.getCustomerId());
        long randomAccNumber = 1000000000L + new Random().nextInt(900000000);
        newAccount.setAccountNumber(randomAccNumber);
        newAccount.setAccountType(AccountsConstants.SAVINGS);
        newAccount.setBranchAddress(AccountsConstants.ADDRESS);
        return newAccount;
    }

    public List<TransactionDto> getTransactionsByAccountNumberAndType(Long accountNumber, String type, Date date) {
        List<Transaction> transactions = transactionService.findByAccountNumberAndType(accountNumber, type, date);
        return transactions.stream().map(TransactionMapper::mapToTransactionDto).toList();
    }
}